#include "Rational.h"
#include "NumberTheory.h"


Rational::Rational(const Integer &numerator, const Integer &denominator, bool coprime)
{
	if (denominator == 0)
		throw;

	if (coprime)
	{
		if (denominator < 0)
		{
			this->numerator = -numerator;
			this->denominator = -denominator;
		}
		else
			this->numerator = numerator;

		this->denominator = denominator;
	}
	else
	{
		const Integer gcd = NumberTheory::GCD(numerator, denominator);

		if (denominator < 0)
		{
			this->numerator = -numerator / gcd;
			this->denominator = -denominator / gcd;
		}
		else
		{
			this->numerator = numerator / gcd;
			this->denominator = denominator / gcd;
		}
	}
}

const Integer Rational::Numerator() const
{
	return std::abs(this->numerator);
}

const Integer& Rational::Denominator() const
{
	return this->denominator;
}

std::string Rational::str() const
{
	if (this->numerator == 0)
		return "0";

	if (this->denominator == 1)
		return this->numerator.str();
	else
		return this->numerator.str() + "/" + this->denominator.str();
}

std::ostream& operator<<(std::ostream &out, const Rational &r)
{
	return out << r.str();
}

bool operator==(const Rational &lhs, const Rational &rhs)
{
	return lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator;
}

bool operator!=(const Rational &lhs, const Rational &rhs)
{
	return !(lhs == rhs);
}

bool operator<(const Rational &lhs, const Rational &rhs)
{
	if (std::sgn(lhs.numerator) < std::sgn(rhs.numerator))
		return true;
	else if (std::sgn(lhs.numerator) > std::sgn(rhs.numerator))
		return false;

	if (std::sgn(lhs.numerator) == 0)
		return false;

	return lhs.numerator * rhs.denominator < lhs.denominator * rhs.numerator;
}

bool operator<=(const Rational &lhs, const Rational &rhs)
{
	return !(lhs > rhs);
}

bool operator>(const Rational &lhs, const Rational &rhs)
{
	return rhs < lhs;
}

bool operator>=(const Rational &lhs, const Rational &rhs)
{
	return !(lhs < rhs);
}

Rational Rational::operator-() const
{
	return Rational(-this->numerator, this->denominator, true);
}

Rational Inverse(const Rational &r)
{
	if (r.numerator == 0)
		throw;

	if (r.numerator > 0)
		return Rational(r.denominator, r.numerator, true);
	else
		return Rational(-r.denominator, -r.numerator, true);
}

Rational operator+(const Rational &lhs, const Rational &rhs)
{
	return Rational(lhs.numerator * rhs.denominator + lhs.denominator * rhs.numerator,
					lhs.denominator * rhs.denominator);
}

Rational& Rational::operator+=(const Rational &addend)
{
	return *this = *this + addend;
}

Rational operator-(const Rational &lhs, const Rational &rhs)
{
	return Rational(lhs.numerator * rhs.denominator - lhs.denominator * rhs.numerator,
					lhs.denominator * rhs.denominator);
}

Rational& Rational::operator-=(const Rational &subtrahend)
{
	return *this = *this - subtrahend;
}

Rational operator*(const Rational &lhs, const Rational &rhs)
{
	return Rational(lhs.numerator * rhs.numerator, lhs.denominator * rhs.denominator);
}

Rational& Rational::operator*=(const Rational &factor)
{
	return *this = *this * factor;
}

Rational Square(const Rational &n)
{
	return Rational(Square(n.numerator), Square(n.denominator), true);
}

Rational operator/(const Rational &lhs, const Rational &rhs)
{
	if (rhs == 0)
		throw;

	return Rational(lhs.numerator * rhs.denominator, lhs.denominator * rhs.numerator);
}

Rational& Rational::operator/=(const Rational &divisor)
{
	return *this = *this / divisor;
}
